/*
 * ============================================================================
 *
 *       Filename:  dynctl.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  2016年05月19日 17时50分52秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  jianxi sun (jianxi), ycsunjane@gmail.com
 *   Organization:  
 *
 * ============================================================================
 */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#include "dynctl_cli.h"

s_config config = {0};

static void init_config(void)
{
	config.command = DYNCTL_UNDEF;
}

static void usage(void)
{
	printf("Usage: dynvethctl server port command [arguments]\n");
	printf("\n");
	printf("commands:\n");
	printf(" status		status\n");
	printf(" bind	username password		bind equipment\n");
	printf(" unbind	username password		unbind equipment\n");
	printf(" newcli nic	new nic client\n");
	printf(" delcli nic	del nic client\n");
	printf(" showcli	show nic client\n");
	printf(" showurl	show url\n");
	printf(" addurl	url	add server url\n");
	printf(" delurl	id	delete server id\n");
	printf("\n");
}

void parse_commandline(int argc, char **argv)
{
	int optoff = 3;
	if ((argc - 3) <= 0) {
		usage();
		exit(1);
	}
	config.server = argv[1];
	config.port = argv[2];
	printf("server: %s, port: %s\n", config.server, config.port);

	if (strcmp(*(argv + optoff), "bind") == 0) {
		config.command = DYNCTL_BIND;
		if ((argc - (optoff + 2)) <= 0) {
			fprintf(stderr, "dynctl: Error: You must "
				"specify username password\n");
			usage();
			exit(1);
		}
		char *username = strdup(*(argv + optoff + 1));
		char *password = strdup(*(argv + optoff + 2));
		int size = strlen(username) + strlen(password) + 2;
		config.param = malloc(size);
		if(!config.param) {
			fprintf(stderr, "Malloc failed\n");
			exit(1);
		}

		config.param[0] = 0;
		strcat(config.param, username);
		strcat(config.param, " ");
		strcat(config.param, password);
		free(username); free(password);
	} else if (strcmp(*(argv + optoff), "newcli") == 0) {
		config.command = DYNCTL_NEWCLI;
		if ((argc - (optoff + 1)) <= 0) {
			fprintf(stderr, "dynctl: Error: You must "
				"specify nic\n");
			usage();
			exit(1);
		}
		char *nic = strdup(*(argv + optoff + 1));
		int size = strlen(nic) + 1;
		config.param = malloc(size);
		if(!config.param) {
			fprintf(stderr, "Malloc failed\n");
			exit(1);
		}

		config.param[0] = 0;
		strcat(config.param, nic);
		free(nic);
	} else if (strcmp(*(argv + optoff), "delcli") == 0) {
		config.command = DYNCTL_DELCLI;
		if ((argc - (optoff + 1)) <= 0) {
			fprintf(stderr, "dynctl: Error: You must "
				"specify nic\n");
			usage();
			exit(1);
		}
		char *nic = strdup(*(argv + optoff + 1));
		int size = strlen(nic) + 1;
		config.param = malloc(size);
		if(!config.param) {
			fprintf(stderr, "Malloc failed\n");
			exit(1);
		}

		config.param[0] = 0;
		strcat(config.param, nic);
		free(nic);
	} else if (strcmp(*(argv + optoff), "showcli") == 0) {
		config.command = DYNCTL_SHOWCLI;
	} else if (strcmp(*(argv + optoff), "unbind") == 0) {
		config.command = DYNCTL_UNBIND;
		if ((argc - (optoff + 2)) <= 0) {
			fprintf(stderr, "dynctl: Error: You must "
				"specify username password\n");
			usage();
			exit(1);
		}
		char *username = strdup(*(argv + optoff + 1));
		char *password = strdup(*(argv + optoff + 2));
		int size = strlen(username) + strlen(password) + 2;
		config.param = malloc(size);
		if(!config.param) {
			fprintf(stderr, "Malloc failed\n");
			exit(1);
		}

		config.param[0] = 0;
		strcat(config.param, username);
		strcat(config.param, " ");
		strcat(config.param, password);
		free(username); free(password);
	} else if (strcmp(*(argv + optoff), "status") == 0) {
		config.command = DYNCTL_STATUS;
	} else if (strcmp(*(argv + optoff), "showurl") == 0) {
		config.command = DYNCTL_SHOWURL;
	} else if (strcmp(*(argv + optoff), "addurl") == 0) {
		config.command = DYNCTL_ADDURL;
		if ((argc - (optoff + 1)) <= 0) {
			fprintf(stderr, "dynctl: Error: You must "
				"specify url\n");
			usage();
			exit(1);
		}

		char *url = strdup(*(argv + optoff + 1));
		int size = strlen(url) + 1;
		config.param = malloc(size);
		if(!config.param) {
			fprintf(stderr, "Malloc failed\n");
			exit(1);
		}

		config.param[0] = 0;
		strcat(config.param, url);
		free(url);
	} else if (strcmp(*(argv + optoff), "delurl") == 0) {
		config.command = DYNCTL_DELURL;
		if ((argc - (optoff + 1)) <= 0) {
			fprintf(stderr, "dynctl: Error: You must "
				"specify url\n");
			usage();
			exit(1);
		}

		char *url = strdup(*(argv + optoff + 1));
		int size = strlen(url) + 1;
		config.param = malloc(size);
		if(!config.param) {
			fprintf(stderr, "Malloc failed\n");
			exit(1);
		}

		config.param[0] = 0;
		strcat(config.param, url);
		free(url);
	} else {
		fprintf(stderr, "dynvethctl: Error: Invalid command"
			" \"%s\"\n", *(argv + optoff));
		usage();
		exit(1);
	}
}

static int send_request(int sock, const char request[])
{
	ssize_t	len, written;

	len = 0;
	while (len != strlen(request)) {
		written = write(sock, (request + len), strlen(request) - len);
		if (written == -1) {
			fprintf(stderr, "Write to dynsocks failed: %s\n",
				strerror(errno));
			exit(1);
		}
		len += written;
	}

	return((int)len);
}
static int connect_to_server(char *server, char *port)
{
	int sock;
	struct sockaddr_in sa_in;

	/* Connect to socket */
	sock = socket(AF_INET, SOCK_STREAM, 0);
	memset(&sa_in, 0, sizeof(sa_in));
	sa_in.sin_family = AF_INET;
	sa_in.sin_addr.s_addr = inet_addr(server);
	sa_in.sin_port = htons(strtol(port, NULL, 10));

	if (connect(sock, (struct sockaddr *)&sa_in,
			sizeof(struct sockaddr_in))) {
		fprintf(stderr, "dynctl: dynsocks probably not started (Error: %s)\n", strerror(errno));
		exit(1);
	}

	return sock;
}

static void dynctl_action(const char cmd[], 
	const char ifyes[], const char ifno[])
{
	int sock;
	char buffer[2048];
	char request[128];
	int len, rlen;

	sock = connect_to_server(config.server, config.port);

	snprintf(request, sizeof(request)-strlen(DYNCTL_TERMINATOR),
		"%s %s", cmd, config.param);
	strcat(request, DYNCTL_TERMINATOR);

	len = send_request(sock, request);

	len = 0;
	memset(buffer, 0, sizeof(buffer));
	while ((len < sizeof(buffer)) && ((rlen = read(sock, (buffer + len),
					(sizeof(buffer) - len))) > 0)) {
		len += rlen;
	}

	if(rlen<0) {
		fprintf(stderr, "dynctl: Error reading socket: %s\n", strerror(errno));
	}

	if (strcmp(buffer, "Yes") == 0) {
		printf(ifyes, config.param);
	} else if (strcmp(buffer, "No") == 0) {
		printf(ifno, config.param);
	} else {
		printf("%s\n", buffer);
	}

	close(sock);
}

static void dynctl_print(const char cmd[])
{
	int sock;
	char buffer[4096];
	char request[128];
	int len;

	sock = connect_to_server(config.server, config.port);

	if(config.param) {
		snprintf(request, 
			sizeof(request)-strlen(DYNCTL_TERMINATOR),
			"%s %s", cmd, config.param);
	} else {
		snprintf(request, 
			sizeof(request)-strlen(DYNCTL_TERMINATOR), 
			"%s", cmd);
	}
	strcat(request, DYNCTL_TERMINATOR);

	len = send_request(sock, request);

	while ((len = read(sock, buffer, sizeof(buffer)-1)) > 0) {
		buffer[len] = '\0';
		printf("%s", buffer);
	}

	if(len<0) {
		fprintf(stderr, "dynctl: Error reading socket: %s\n", 
			strerror(errno));
	}

	close(sock);
}

static void dynctl_bind(void)
{
	dynctl_action("bind",
		"bind %s success\n",
		"bind %s failed\n"
		);
}

static void dynctl_unbind(void)
{
	dynctl_action("unbind",
		"unbind %s success\n",
		"unbind %s failed\n"
		);
}

static void dynctl_newcli(void)
{
	dynctl_action("newcli",
		"newcli %s success\n",
		"newcli %s failed\n"
		);
}

static void dynctl_delcli(void)
{
	dynctl_action("delcli",
		"delcli %s success\n",
		"delcli %s failed\n"
		);
}

static void dynctl_showcli(void)
{
	dynctl_print("showcli");
}

static void dynctl_status(void)
{
	dynctl_print("status");
}

static void dynctl_addurl(void)
{
	dynctl_action("addurl",
		"addurl %s success\n",
		"addurl %s failed\n"
		);
}

static void dynctl_delurl(void)
{
	dynctl_action("delurl",
		"delurl %s success\n",
		"delurl %s failed\n"
		);
}

static void dynctl_showurl(void)
{
	dynctl_print("showurl");
}

int main(int argc, char **argv)
{
	init_config();
	parse_commandline(argc, argv);

	switch(config.command) {
	case DYNCTL_ADDURL:
		dynctl_addurl();
		break;
	case DYNCTL_DELURL:
		dynctl_delurl();
		break;
	case DYNCTL_SHOWURL:
		dynctl_showurl();
		break;
	case DYNCTL_BIND:
		dynctl_bind();
		break;
	case DYNCTL_UNBIND:
		dynctl_unbind();
		break;
	case DYNCTL_NEWCLI:
		dynctl_newcli();
		break;
	case DYNCTL_DELCLI:
		dynctl_delcli();
		break;
	case DYNCTL_SHOWCLI:
		dynctl_showcli();
		break;
	case DYNCTL_STATUS:
		dynctl_status();
		break;
	default:
		fprintf(stderr, "Unknown opcode: %d\n", 
			config.command);
		exit(1);
		break;
	}
	exit(0);
}
